<template>
  <van-action-sheet
    v-model:show="visible"
    :title="buttonInfo?.operation"
    :closeable="false"
    cancel-text=""
    :close-on-click-overlay="false"
    :style="{ height: '80%' }"
    @cancel="cancleApprove"
  >
    <van-form ref="approveParamsRef">
      <!-- 转交 -->
      <van-field
        v-if="openTransferAndCountersign"
        v-model="selectedUserName"
        name="transferUser"
        :label="labelTitle"
        :readonly="true"
        :rules="[{ required: true, message: '转交人员不能为空' }]"
        :input-align="FieldInputAlignEnum.RIGHT"
        :error-message-align="FieldInputAlignEnum.RIGHT"
        is-link
        @click="openTransfer"
      >
      </van-field>

      <!-- 退回 -->
      <van-field
        v-if="showBackNode"
        v-model="backNodeName"
        name="backNode"
        :label="labelTitle"
        :readonly="true"
        :rules="[{ required: true, message: '退回节点不能为空' }]"
        :input-align="FieldInputAlignEnum.RIGHT"
        :error-message-align="FieldInputAlignEnum.RIGHT"
        is-link
        @click="showBackNodePicker = true"
      >
      </van-field>

      <!-- 常用意见 -->
      <van-cell
        is-link
        :border="false"
        title="常用意见"
        v-model="commonOpinion"
        @click="showOpinion = true"
      />
      <van-field
        v-model="commentInfo.opinion"
        rows="2"
        autosize
        label="添加意见"
        type="textarea"
        :input-align="FieldInputAlignEnum.RIGHT"
        :error-message-align="FieldInputAlignEnum.RIGHT"
      />

      <!-- 图片 -->
      <van-field
        label="添加图片"
        name="uploadImage"
        :input-align="FieldInputAlignEnum.RIGHT"
        :error-message-align="FieldInputAlignEnum.RIGHT"
      >
        <template #input>
          <van-uploader
            v-model="imageList"
            :multiple="true"
            :max-count="10"
            :max-size="ImageUpload.maxSize * 1024 * 1024"
            accept="image/*"
            upload-text="选择图片"
            :before-read="beforeImageRead"
            :after-read="afterImageRead"
            @delete="deleteImage"
          />
        </template>
      </van-field>

      <!-- 附件 -->
      <van-field
        label="添加附件"
        name="uploadFile"
        :input-align="FieldInputAlignEnum.RIGHT"
        :error-message-align="FieldInputAlignEnum.RIGHT"
      >
        <template #input>
          <van-uploader
            v-model="fileList"
            :multiple="true"
            :max-count="5"
            :max-size="FileUpload.maxSize * 1024 * 1024"
            accept=""
            upload-text="选择附件"
            :before-read="beforeFileRead"
            :after-read="afterFileRead"
            @delete="deleteFile"
          />
        </template>
      </van-field>

      <!-- 底部操作按钮 -->
      <aster-tabbar
        :customStyle="{
          borderTopLeftRadius: '10px',
          borderTopRightRadius: '10px',
        }"
      >
        <van-button block @click="handleCancel">取消</van-button>
        <van-button type="primary" block @click="handleSubmit">提交</van-button>
      </aster-tabbar>
    </van-form>
  </van-action-sheet>

  <!-- 选人组件 -->
  <select-user ref="selectUserRef" :multiple="false" @confirm="handleConfirmUser" />
  <!-- 选择退回节点 -->
  <van-popup v-model:show="showBackNodePicker" round position="bottom">
    <van-picker
      title="请选择"
      :model-value="[approveParams.backNode || '']"
      :columns="backNodeList"
      :columns-field-names="{
        text: 'nodeName',
        value: 'nodeId',
        children: 'children',
      }"
      @cancel="showBackNodePicker = false"
      @confirm="handleConfirmBack"
    />
  </van-popup>
  <!-- 常用意见sheet -->
  <van-action-sheet
    v-model:show="showOpinion"
    :actions="opinionOptions"
    cancel-text="取消"
    close-on-click-action
    @select="commonOpinionChange"
  />
</template>
<script setup lang="ts">
  import { computed, PropType, ref, watchEffect } from 'vue';
  import { completeTaskApi, getRecallTaskNodesApi } from '@/api/workflow/task';
  import { isDef, isEmpty, isNotEmpty } from '@/utils/is';
  import { FieldInputAlignEnum } from '@/enums/formEnum';
  import SelectUser from '../process/select-user.vue';
  import AsterTabbar from '@/components/common/aster-tabbar.vue';
  import { opinionOptions } from '@/enums/workFlowEnum';
  import { ImageUpload, FileUpload } from '@/utils/config/fileConfig';
  import { Toast } from '@/utils/uniapi/prompt';
  import { uploadFile } from '@/utils/fileUtils';

  const emits = defineEmits(['confirm', 'cancel']); // 关闭详情弹框
  const props = defineProps({
    formData: {
      type: Object as PropType<WorkForm.FormDataModel>,
      default: () => ({}),
    },
    // 任务id，有可能为空
    taskId: {
      type: String,
      default: '',
    },
    // 表单id
    formId: {
      type: String,
      default: '',
    },
  });

  // 是否显示
  const visible = ref(false);
  // 已选择的人员
  const selectedInfos = ref<string[]>([]);
  // 是否显示转交、加签，两个通用
  const openTransferAndCountersign = ref(false);
  // 是否显示退回
  const showBackNode = ref(false);
  const showBackNodePicker = ref(false);
  // 退回/转交/加签 label
  const labelTitle = ref('');
  // 按钮信息，包括操作名称、操作类型、操作权限
  const buttonInfo = ref<WorkForm.ButtonPermission>();
  // 常用意见
  const showOpinion = ref(false);
  // 选中的常用意见
  const commonOpinion = ref('');
  // 可退回的节点名称
  const backNodeName = ref('');
  // 可退回的节点列表
  const backNodeList = ref<Process.BackNodeModel[]>([]);
  // 选中的用户名称
  const selectedUserName = ref('');
  const operationTypeInfo = ref<WorkForm.OperationType>({
    approveType: '', // 审批类型
    targetId: '', // 目标id
    targetName: '', // 目标名称
    targetType: '', // 目标类型
  });
  // 审批意见
  const commentInfo = ref<WorkForm.Comment>({
    opinion: '',
    imageList: [],
    fileList: [],
    operationType: operationTypeInfo.value,
  });
  // 图片
  const imageList = ref<any>([]);
  // 附件
  const fileList = ref<any>([]);

  // 提交参数
  const approveParams = ref<WorkForm.ApproveParams>({
    taskId: '', // 任务id
    approveType: 'agree',
    signature: '', // 手写签名,base64
    formData: props.formData, // 表单数据
    formId: '', // 表单id
    backNode: '', // 退回节点
    transferUser: '', // 转交人
    countersignUser: '', // 会签人
    comment: {
      opinion: '', // 审批意见
      imageList: [],
      fileList: [],
      operationType: operationTypeInfo.value,
    },
  });
  // 注册组件
  const approveParamsRef = ref();
  const selectUserRef = ref();

  const handleCancel = () => {
    visible.value = false;
    resetApproveParams();
    emits('cancel');
  };

  /**
   * @description: 保存
   * @return {*}
   */
  const handleSubmit = () => {
    approveParamsRef.value.validate().then(() => {
      commentInfo.value.operationType = operationTypeInfo.value;
      approveParams.value.taskId = props.taskId;
      approveParams.value.formId = props.formId;
      approveParams.value.comment = commentInfo.value;
      completeTaskApi(approveParams.value).then(() => {
        visible.value = false;
        // 处理成功，关闭详情弹框
        emits('confirm');
        // 重置表单数据，防止下次提交还有数据
        resetApproveParams();
      });
    });
  };

  /**
   * 取消按钮，也对数据进行重置
   */
  const cancleApprove = () => {
    visible.value = false;
    resetApproveParams();
  };

  /**
   * 重置表单数据
   */
  const resetApproveParams = () => {
    commentInfo.value = {
      opinion: '', // 审批意见
      imageList: [],
      fileList: [],
    };
    operationTypeInfo.value = {
      approveType: '', // 审批类型
      targetId: '', // 目标id
      targetName: '', // 目标名称
      targetType: '', // 目标类型
    };
    approveParams.value = {
      comment: {
        ...commentInfo.value,
        ...operationTypeInfo.value,
      },
      taskId: '', // 任务id
      approveType: 'agree',
      signature: '', // 手写签名,base64
      formData: props.formData, // 表单数据
      formId: '', // 表单id
      backNode: '', // 退回节点
      transferUser: '', // 转交人
    };
    selectedInfos.value = [];
    selectedUserName.value = '';
    openTransferAndCountersign.value = false;
    showBackNode.value = false;
    labelTitle.value = '';
    commonOpinion.value = '';
    backNodeName.value = '';
  };

  /**
   * @description: 常用意见选中值改变时赋值给审批意见
   * @param val
   */
  const commonOpinionChange = (item: any) => {
    showOpinion.value = false;
    commentInfo.value.opinion = item.name;
    commentInfo.value.operationType = operationTypeInfo.value;
  };

  /**
   * @description: 选中退回节点
   * @param val
   */
  const handleConfirmBack = (event: any) => {
    backNodeName.value = event.selectedOptions[0].nodeName;
    const val = event.selectedValues[0];
    backNodeList.value.forEach((item: Process.BackNodeModel) => {
      if (item.nodeId === val) {
        // 如果是退回，给审批意见赋值
        operationTypeInfo.value.approveType = approveParams.value.approveType;
        operationTypeInfo.value.targetId = item.nodeId;
        operationTypeInfo.value.targetName = item.nodeName;
        operationTypeInfo.value.targetType = 'node'; // user | node
        commentInfo.value.operationType = operationTypeInfo.value;
      }
    });
    approveParams.value.backNode = val;
    showBackNodePicker.value = false;
  };

  /**
   * 转交选人组件
   */
  const openTransfer = () => {
    selectUserRef.value.init(selectedInfos.value);
  };

  /**
   * @description: 选人选部门设置默认值组件回调
   * @param {*} val 选中人员
   * @return {*}
   */
  const handleConfirmUser = (val: User.UserDeptModel[]) => {
    if (val.length > 0) {
      val.forEach((item: User.UserDeptModel) => {
        selectedInfos.value.push(item.id);
        selectedUserName.value = item.name;
        // 如果是转交或加签
        operationTypeInfo.value.approveType = approveParams.value.approveType;
        operationTypeInfo.value.targetId = item.id;
        operationTypeInfo.value.targetName = selectedUserName.value;
        operationTypeInfo.value.targetType = 'user'; // user | node
        commentInfo.value.operationType = operationTypeInfo.value;
        approveParams.value.transferUser = item.id;
      });
    } else {
      selectedInfos.value = [];
      selectedUserName.value = '';
    }
  };

  /**
   * @description: 图片上传前校验
   * @param {any} rawFile 图片
   * @return {*}
   */
  const beforeImageRead = (rawFile) => {
    // 判断附件类型
    if (isEmpty(rawFile.type) || !ImageUpload.type.includes(rawFile.type)) {
      Toast('上传图片的格式不符合要求');
      return false;
    } else if (rawFile.size / 1024 / 1024 > ImageUpload.maxSize) {
      // 判断附件大小
      Toast('上传图片大小不能超过' + ImageUpload.maxSize + 'MB');
      return false;
    }
    return true;
  };

  /**
   * 删除图片
   * @param file 图片
   * @param detail 索引
   */
  const deleteImage = (file, detail) => {
    if (commentInfo.value.imageList) {
      commentInfo.value.imageList.splice(detail.index, 1);
    }
  };

  /**
   * @description: 上传图片
   * @param {*} rawFile
   * @return {*}
   */
  const afterImageRead = (rawFile: any) => {
    if (Array.isArray(rawFile)) {
      rawFile.forEach((file) => {
        handleUploadFile(file, 'image');
      });
    } else {
      handleUploadFile(rawFile, 'image');
    }
  };

  // 附件类型
  const _acceptList = computed(() => {
    let accepts: string[] = [];
    accepts.push(...FileUpload.type.image);
    accepts.push(...FileUpload.type.video);
    accepts.push(...FileUpload.type.audio);
    accepts.push(...FileUpload.type.doc);
    accepts.push(...FileUpload.type.zip);
    accepts.push(...FileUpload.type.other);
    return accepts;
  });

  /**
   * @description: 附件上传前校验
   * @param {any} rawFile 附件
   * @return {*}
   */
  const beforeFileRead = (rawFile) => {
    // 判断附件类型
    if (isEmpty(rawFile.type) || !_acceptList.value) {
      Toast('上传文件的格式不符合要求');
      return false;
    }
    if (isNotEmpty(_acceptList.value) && !_acceptList.value.includes(rawFile.type)) {
      Toast('不支持上传此类型的文件');
      return false;
    }
    // 判断附件大小
    if (rawFile.size / 1024 / 1024 > FileUpload.maxSize) {
      Toast('上传文件大小不能超过' + FileUpload.maxSize + 'MB');
      return false;
    }
    return true;
  };

  /**
   * @description: 上传附件
   * @param {*} rawFile
   * @return {*}
   */
  const afterFileRead = (rawFile: any) => {
    if (Array.isArray(rawFile)) {
      rawFile.forEach((file) => {
        handleUploadFile(file, 'file');
      });
    } else {
      handleUploadFile(rawFile, 'file');
    }
  };

  /**
   * 删除附件
   * @param file 附件
   * @param detail 索引
   */
  const deleteFile = (file, detail) => {
    if (commentInfo.value.fileList) {
      commentInfo.value.fileList.splice(detail.index, 1);
    }
  };

  /**
   * 上传附件
   * @param file 附件
   */
  const handleUploadFile = (rawFile: any, type: string) => {
    uploadFile(rawFile.file, (data) => {
      const fileObj: WorkForm.FileModel = {
        name: data.name,
        url: data.url,
      };
      if (type === 'image' && commentInfo.value.imageList) {
        commentInfo.value.imageList.push(fileObj);
      }
      if (type === 'file' && commentInfo.value.fileList) {
        commentInfo.value.fileList.push(fileObj);
      }
    });
  };

  watchEffect(() => {
    if (isNotEmpty(buttonInfo.value) && isDef(buttonInfo.value)) {
      // 根据按钮类型判断显示哪些组件
      switch (buttonInfo.value?.name) {
        case 'agree': // 同意
        case 'disagree': // 不同意
        case 'revoke': // 撤销
          break;
        case 'forward': // 转交
          openTransferAndCountersign.value = true;
          labelTitle.value = '转交人';
          break;
        case 'afterAddSign': // 加签
          openTransferAndCountersign.value = true;
          labelTitle.value = '加签人';
          break;
        case 'recall': // 退回
          showBackNode.value = true;
          labelTitle.value = '退回节点';
          break;
        default:
          throw new Error(`未知按钮类型：${buttonInfo.value?.name}`);
      }
    }
  });

  /**
   * 查询是否有可退回节点
   */
  const getBackNodeList = () => {
    showBackNode.value = true;
    labelTitle.value = '退回节点';
    getRecallTaskNodesApi(props.taskId).then(({ data }) => {
      backNodeList.value = data;
    });
  };

  // 初始化显示
  const init = (item: WorkForm.ButtonPermission) => {
    console.log('approve--->', item);
    visible.value = true;
    buttonInfo.value = item;
    approveParams.value.approveType = buttonInfo.value?.name;
    operationTypeInfo.value.approveType = approveParams.value.approveType;
    if (approveParams.value.approveType === 'recall') {
      getBackNodeList();
    }
  };

  defineExpose({
    init,
  });
</script>
<style scoped lang="scss"></style>
